package com.hxk.juc.collection;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 *多线程情况下操作map集合案例
 *     HashMap里面实现一个静态内部类Entry，其重要的属性有 hash，key，value，next。
     * HashMap里面用到链式数据结构的一个概念。
     * 上面我们提到过Entry类里面有一个next属性，
     * 作用是指向下一个Entry。打个比方，
     * 第一个键值对A进来，通过计算其key的hash得到的index=0，记做:Entry[0] = A。
     * 一会后又进来一个键值对B，通过计算其index也等于0，现在怎么办？
     * HashMap会这样做:B.next = A,Entry[0] = B,
     * 如果又进来C,index也等于0,那么C.next = B,Entry[0] = C；
     * 这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。
 *      数组索引的计算过程，如下，A是要put的key
 *          String[] tab = new String[16];
            int index = ("A".hashCode() & 0x7FFFFFFF);
            int aa = index % tab.length;
            System.out.println("A".hashCode()+">>>>"+index+">>>>>"+aa);
 */
public class ManyThreadMapCase {

    public static void main(String[] args) {
        //singleThreadMap();

        //manyThreadNotSafe();

        //manyThreadSafe1();

        //manyThreadSafe2();

        manyThreadSafe3();
    }

    /**
     * 在单线程情况下使用set，在put时没有任何问题
     */
    private static void singleThreadMap() {
        //HashMap在jdk1.8前的底层是由数组和链表来实现对数据的存储来实现的，默认初始长度为16，动态扩容时是原来的2倍，他的扩容因子为0.75
        //jdk1.8后：HashMap采用数组+链表+红黑树实现
        Map<String, String> map = new HashMap<>();
        map.put("a","1");
        map.put("b","2");
        map.put("c","3");
        map.put("d","4");
        map.forEach((key, val)->{
            System.out.println(key+">>>>"+val);
        });
    }

    /**
     * 在多线程情况下HashMap是线程不安全
     *      在put时会出现ConcurrentModificationException
     */
    private static void manyThreadNotSafe() {
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,8));
                System.out.println(Thread.currentThread().getName()+"\t"+map);
            },String.valueOf(i)).start();
        }
    }

    /**
     * 处理多线程下HashMap线程不安全方式1：
     * 使用HashMap对应的线程安全集合类Hashtable
     *      在Hashtable中的put方法加了synchronized对象锁在put时候锁住整个Hashtable对象
     */
    private static void manyThreadSafe1(){
        Map<String,String> map = new Hashtable<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,8));
                System.out.println(Thread.currentThread().getName()+"\t"+map);
            },String.valueOf(i)).start();
        }
    }
    /**
     *处理多线程下HashMap线程不安全方式2：
     * 使用集合工具类Collections提供的Collections.synchronizedMap(new HashMap<>())方法获取线程安全的HashMap
     *      使用Collections.synchronizedMap(new HashMap<>())方法获取的HashMap，
     *      在添加时方法内部使用synchronized(mutex)锁住了一个Object mutex对象
     *      在这里锁的颗粒度变小了，锁的不是整个对象。从性能上优于上面的方法
     */
    private static void manyThreadSafe2(){
        Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,8));
                System.out.println(Thread.currentThread().getName()+"\t"+map);
            },String.valueOf(i)).start();
        }
    }
    /**
     *处理多线程下HashMap线程不安全方式3：
     * 使用jdk1.5后新加入的juc中的ConcurrentHashMap线程安全类
     *      在ConcurrentHashMap中的put方法使用了synchronized(node)锁，锁住链表，
     *      让锁的颗粒度变得更小，目前是性能上最优的解决方案
     */
    private static void manyThreadSafe3(){
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,8));
                System.out.println(Thread.currentThread().getName()+"\t"+map);
            },String.valueOf(i)).start();
        }
    }

}
